{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from numba import njit\n",
    "from numpy.typing import NDArray"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  _verify_input 函数\n",
    "验证输入参数是否有效。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 名为 _verify_input 的函数，其作用是对 sumv 函数的输入参数进行验证。\n",
    "@njit\n",
    "def _verify_input(array: NDArray[np.float_], n: int):\n",
    "    '''\n",
    "    定义了 _verify_input 函数，它接受两个参数：\n",
    "        array：一个 NumPy 数组，类型注解为 NDArray[np.float_]，表示数组元素为浮点数。\n",
    "        n：一个整数，表示周期长度。\n",
    "    '''\n",
    "    if n <= 0:  # 检查整数 n 是否小于或等于0。如果是，抛出一个 ValueError，提示 n 需要大于等于1。\n",
    "        raise ValueError(\"n needs to be >= 1.\")\n",
    "    if n > len(array):  #   检查整数 n 是否大于数组 array 的长度。\n",
    "        raise ValueError(\"n is greater than array length.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  lowv 函数\n",
    "计算输入数组 array 中每 n 个元素的最小值（即每 n 个元素的最低点）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这个函数的目的是找到输入数组 array 中每 n 个连续元素中的最小值，并将这些最小值存储在输出数组 out 中。\n",
    "@njit\n",
    "def lowv(array: NDArray[np.float_], n: int) -> NDArray[np.float_]:\n",
    "    '''\n",
    "    lowv 函数，它接受两个参数：\n",
    "        array：一个 NumPy 数组，类型为 np.float_（浮点数）。\n",
    "        n：一个整数，表示要计算的周期长度。\n",
    "    '''\n",
    "    \"\"\"Calculates the lowest values for every ``n`` period in ``array``.\n",
    "\n",
    "    Args:\n",
    "        array: :class:`numpy.ndarray` of data.\n",
    "        n: Length of period.\n",
    "\n",
    "    Returns:\n",
    "        :class:`numpy.ndarray` of the lowest values for every ``n`` period in\n",
    "        ``array``.\n",
    "    \"\"\"\n",
    "    if not len(array):# 检查输入数组 array 是否为空。如果为空，则返回一个空的 NumPy 数组。\n",
    "        return np.array(tuple())\n",
    "    _verify_input(array, n) # 验证输入参数 array 和 n 是否有效。\n",
    "    out_len = len(array)    # 计算输入数组 array 的长度，并将其存储在变量 out_len 中。\n",
    "    \n",
    "    # 创建一个新的 NumPy 数组 out，其长度与输入数组相同，初始值为 np.nan（表示“不是一个数字”的浮点数值）。\n",
    "    # for _ in ... 循环中的下划线 _ 是一个占位符\n",
    "    # 使用下划线作为变量名是一种避免命名冲突和提高代码清晰度的方法，它表明循环中的变量不会被进一步使用或引用\n",
    "    out = np.array([np.nan for _ in range(out_len)])\n",
    "    \n",
    "    \n",
    "    for i in range(n, out_len + 1): # 循环遍历从 n 到 out_len + 1 的整数。\n",
    "        # 在循环中，对于每个索引 i，计算从 i - n 到 i（不包括 i）的子数组的最小值，\n",
    "        # 并将这个最小值赋值给 out 数组的索引 i - 1 位置。\n",
    "        out[i - 1] = np.min(array[i - n : i])\n",
    "    return out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  highv 函数\n",
    "计算输入数组 array 中每 n 个元素的最大值（即每 n 个元素的最高点）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit\n",
    "def highv(array: NDArray[np.float_], n: int) -> NDArray[np.float_]:\n",
    "    \"\"\"Calculates the highest values for every ``n`` period in ``array``.\n",
    "\n",
    "    Args:\n",
    "        array: :class:`numpy.ndarray` of data.\n",
    "        n: Length of period.\n",
    "\n",
    "    Returns:\n",
    "        :class:`numpy.ndarray` of the highest values for every ``n`` period in\n",
    "        ``array``.\n",
    "    \"\"\"\n",
    "    if not len(array):\n",
    "        return np.array(tuple())\n",
    "    _verify_input(array, n)\n",
    "    out_len = len(array)\n",
    "    out = np.array([np.nan for _ in range(out_len)])\n",
    "    for i in range(n, out_len + 1):\n",
    "        out[i - 1] = np.max(array[i - n : i])\n",
    "    return out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  sumv 函数\n",
    "计算输入数组 array 中每 n 个元素的和。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "'''\n",
    "这个函数的目的是计算输入数组 array 中每 n 个元素的累加和，并将这些和存储在输出数组 out 中。\n",
    "如果输入数组长度小于 n，则输出数组的前 n - 1 个元素将保持为 np.nan，因为没有足够的数据来计算这些位置的累加和。\n",
    "'''\n",
    "@njit\n",
    "def sumv(array: NDArray[np.float_], n: int) -> NDArray[np.float_]:\n",
    "    # 定义了 sumv 函数，它接受两个参数：\n",
    "    #   array：一个 NumPy 数组，类型为 np.float_（浮点数）。\n",
    "    #   n：一个整数，表示要计算的周期长度。\n",
    "    \"\"\"Calculates the sums for every ``n`` period in ``array``.\n",
    "\n",
    "    Args:\n",
    "        array: :class:`numpy.ndarray` of data.\n",
    "        n: Length of period.\n",
    "\n",
    "    Returns:\n",
    "        :class:`numpy.ndarray` of the sums for every ``n`` period in ``array``.\n",
    "    \"\"\"\n",
    "    if not len(array):  # 检查输入数组 array 是否为空。如果为空，则返回一个空的 NumPy 数组。\n",
    "        # tuple() 是 Python 中的一个内置函数，用于创建一个元组（tuple）。\n",
    "        # 元组是一个不可变的序列类型，与列表（list）类似，但列表是可变的，而元组则不是。\n",
    "        return np.array(tuple())\n",
    "    _verify_input(array, n) #这个函数可能检查 array 和 n 是否满足某些条件。检查输入参数有效性的函数。\n",
    "    out_len = len(array)    # 计算输入数组 array 的长度，并将其存储在变量 out_len 中。\n",
    "    # 创建一个新的 NumPy 数组 out，其长度与输入数组相同，初始值为 np.nan（表示“不是一个数字”的浮点数值）。\n",
    "    out = np.array([np.nan for _ in range(out_len)])\n",
    "    for i in range(n, out_len + 1): # 循环遍历从 n 到 out_len + 1 的整数。\n",
    "                                    # 这里的 n 是因为我们在计算前 n - 1 个元素时没有足够的数据来形成一个完整的周期。\n",
    "        out[i - 1] = np.sum(array[i - n : i])   # 对于每个索引 i，计算从 i - n 到 i（不包括 i）的子数组的和，\n",
    "                                                # 并将这个和赋值给 out 数组的索引 i - 1 位置。\n",
    "    return out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  cross 函数\n",
    "计算输入两列sumv数组 array 中每个元素是否存在交叉点"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "@njit   # Numba 库的 just-in-time 编译器的装饰器。\n",
    "        # 它用于将 Python 函数编译为机器代码，以提高执行速度。njit 特别适用于数值计算密集型任务。\n",
    "def cross(a: NDArray[np.float_], b: NDArray[np.float_]) -> NDArray[np.bool_]:\n",
    "    # 定义了 cross 函数，它接受两个参数 a 和 b，这两个参数都是 NumPy 数组，类型为 np.float_（浮点数）。\n",
    "    # 函数返回值的类型为 np.bool_（布尔值）。\n",
    "    \"\"\"Checks for crossover of ``a`` above ``b``.\n",
    "\n",
    "    Args:\n",
    "        a: :class:`numpy.ndarray` of data.\n",
    "        b: :class:`numpy.ndarray` of data.\n",
    "\n",
    "    Returns:\n",
    "        :class:`numpy.ndarray` containing values of ``1`` when ``a`` crosses\n",
    "        above ``b``, otherwise values of ``0``.\n",
    "    \"\"\"\n",
    "    if not len(a):\n",
    "        raise ValueError(\"a cannot be empty.\")  # 检查数组 a 是否为空。\n",
    "    if not len(b):\n",
    "        raise ValueError(\"b cannot be empty.\")  # 检查数组 b 是否为空。\n",
    "    if len(a) != len(b):\n",
    "        raise ValueError(\"len(a) != len(b)\")    # 确保两个数组的长度相同。\n",
    "    if len(a) < 2:\n",
    "        raise ValueError(\"a and b must have length >= 2.\")\n",
    "    crossed = np.where(a > b, 1, 0)\n",
    "    return (sumv(crossed > 0, 2) == 1) * crossed    # : 确保两个数组的长度至少为2，因为至少需要两个元素来检测交叉。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### crossed = np.where(a > b, 1, 0)\n",
    "np.where 可以有三种不同的用法，但在这里它用作一个三元操作符的等价物，类似于Python中的 if-else 结构。\n",
    "\n",
    "np.where 的作用是：\n",
    "\n",
    "检查数组 a 和 b 中的对应元素。\n",
    "对于每一个位置，如果 a 中的元素大于 b 中的元素（即 a[i] > b[i]），则在 crossed 中对应的位置放置 1。\n",
    "如果 a 中的元素不大于 b 中的元素，则在 crossed 中对应的位置放置 0。\n",
    "所以，crossed 将是一个与 a 和 b 形状相同的数组，其中包含了 1（表示 a 中的元素在对应位置大于 b）或 0（表示不大于）。\n",
    "\n",
    "这个操作在比较两个时间序列、检测交叉点或其他类似场景时非常有用。但请注意，这个操作并不直接检测交叉点（即从一个值的下方穿越到上方），而是简单地比较两个数组在每个位置上的值。为了检测交叉点，你通常需要比较相邻的元素，如我之前提供的修正后的示例中所示。\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
